package cz.proks.easyhelpdesk.gae.api.impl.db;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Scope;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import cz.proks.easyhelpdesk.api.HelpdeskMemberApi;
import cz.proks.easyhelpdesk.api.ThreadMemberApi;
import cz.proks.easyhelpdesk.api.config.Configuration;
import cz.proks.easyhelpdesk.api.exception.ApiException;
import cz.proks.easyhelpdesk.api.model.HelpdeskMember;
import cz.proks.easyhelpdesk.api.model.HelpdeskRole;
import cz.proks.easyhelpdesk.gae.api.impl.dbo.ThreadMember;
import cz.proks.easyhelpdesk.gae.api.impl.dbo.Ticket;

@Repository(Configuration.DB.HELPDESK_MEMBER_API)
@Scope("prototype")
public class HelpdeskMemberApiImpl extends BaseApiImpl<HelpdeskMember> implements HelpdeskMemberApi {
    private final Logger log = LoggerFactory.getLogger(getClass());

    @Autowired
    @Qualifier(Configuration.DB.THREAD_MEMBER_API)
    private ThreadMemberApi threadMemberApi;
    
    @Override
    protected Class<HelpdeskMember> getDomainClass() {
        return HelpdeskMember.class;
    }
    
    @Override
    @Transactional(readOnly = false)
    public Long create(HelpdeskMember entity) throws ApiException {
        log.debug("create():: Start with entity '{}'", entity);
        
        ensureHelpdeskRoles();
        Long id = super.create(entity);
        
        log.debug("create():: End with id '{}'", id);
        return id;
    }
    
    
    
    @Override
    @Transactional(readOnly = false)
    public void remove(Long id) throws ApiException {
        log.debug("remove():: Start with id '{}'", id);
        
        EntityManager entityManager = getEntityManager();
        Query listThreadMembersQuery = entityManager.createNamedQuery("ThreadMember.listForHelpdeskMember");
        listThreadMembersQuery.setParameter("helpdeskMemberId", id);
        List<ThreadMember> dboThreadMembers = listThreadMembersQuery.getResultList();
        
        if (dboThreadMembers != null)
            for (ThreadMember dboThreadMember : dboThreadMembers) {
                entityManager.remove(dboThreadMember);
            }
        
        super.remove(id);
        
        log.debug("remove():: Exit");       
    }
    
    @Override
    @Transactional(readOnly = false)
    public void update(HelpdeskMember entity) throws ApiException {
        log.debug("update():: Start with entity {}", entity);
        
        EntityManager entityManager = getEntityManager();
        
        if (hasRole(HelpdeskRole.CUSTOMER, entity.getHelpdeskRoles())) {
            
            Query query = entityManager.createNamedQuery("ThreadMember.listForHelpdeskMember");
            query.setParameter("helpdeskMemberId", entity.getId());
            List<ThreadMember> dboThreadMembers = query.getResultList();
            
            if (dboThreadMembers != null)
                for (ThreadMember dboThreadMember : dboThreadMembers) {
                    if (!containsHelpdeskMember(entity, dboThreadMember.getThread().getThreadTickets()))
                        entityManager.remove(dboThreadMember);
                }            
        } else if (hasRole(HelpdeskRole.OPERATOR, entity.getHelpdeskRoles()))
            threadMemberApi.shareForHelpdeskMemberAndRole(entity.getId(), HelpdeskRole.OPERATOR);        
        
        super.update(entity);
        
        log.debug("update():: Exit");
    }

    @Override
    protected HelpdeskMember getDboObject(HelpdeskMember entity) {
        cz.proks.easyhelpdesk.gae.api.impl.dbo.HelpdeskMember dbo = instantiateDbo(entity);

        if (dbo != null)
            dbo.toDbo(entity);

        return dbo;
    }

    @Override
    @Transactional(readOnly = true)
    public HelpdeskMember getForUserAndHelpdesk(Long userId, Long helpdeskId) throws ApiException {
        log.debug("getForUserAndHelpdesk():: Start with userId '{}', helpdeskId '{}'", userId, helpdeskId);
        
        HelpdeskMember helpdeskMember = null;
        
        Query query = getEntityManager().createNamedQuery("HelpdeskMember.get");
        query.setParameter("userId", userId);
        query.setParameter("helpdeskId", helpdeskId);        
        
        try {
            cz.proks.easyhelpdesk.gae.api.impl.dbo.HelpdeskMember dbo =  (cz.proks.easyhelpdesk.gae.api.impl.dbo.HelpdeskMember) query.getSingleResult();
            helpdeskMember = fromDbo(dbo);
        } catch (EmptyResultDataAccessException erdae) {            
        }
                 
        log.debug("getForUserAndHelpdesk():: Exit with entity '{}'", helpdeskMember);
        return helpdeskMember;
    }

    @Override
    @Transactional(readOnly = true)
    public List<HelpdeskMember> listForUser(Long userId) throws ApiException {
        log.debug("listForUser():: Start with userId '{}'", userId);
        
        List<HelpdeskMember> entityList = null;
        
        Query query = getEntityManager().createNamedQuery("HelpdeskMember.listForUser");
        query.setParameter("userId", userId);        
        
        try {
            List<cz.proks.easyhelpdesk.gae.api.impl.dbo.HelpdeskMember> dboList = query.getResultList();
            entityList = fromDboList(dboList);
        } catch (EmptyResultDataAccessException erdae) {
            entityList = new LinkedList<HelpdeskMember>();
        }
                 
        log.debug("listForOwner():: Exit with {} entities", entityList != null ? entityList.size() : null);
        return entityList;
    }    
    
    @Override
    @Transactional(readOnly = true)
    public List<HelpdeskMember> listForHelpdesk(Long helpdeskId) throws ApiException {
        log.debug("listForHelpdesk():: Start with helpdeskId '{}'", helpdeskId);
        
        List<HelpdeskMember> entityList = null;
        
        Query query = getEntityManager().createNamedQuery("HelpdeskMember.listForHelpdesk");
        query.setParameter("helpdeskId", helpdeskId);        
        
        try {
            List<cz.proks.easyhelpdesk.gae.api.impl.dbo.HelpdeskMember> dboList = query.getResultList();
            entityList = fromDboList(dboList);
        } catch (EmptyResultDataAccessException erdae) {
            entityList = new LinkedList<HelpdeskMember>();
        }
                 
        log.debug("listForHelpdesk():: Exit with {} entities", entityList != null ? entityList.size() : null);
        return entityList;
    }
    
    @Override
    @Transactional(readOnly = false)
    public void addHelpdeskRole(Long id, HelpdeskRole helpdeskRole) throws ApiException {
        log.debug("addHelpdeskRole():: Start with id '{}', helpdeskRole '{}'", id, helpdeskRole);        
        
        HelpdeskMember helpdeskMember = get(id);
        
        if (helpdeskMember == null)
            return;
        
        if (helpdeskRoleExist(helpdeskRole, helpdeskMember.getHelpdeskRoles()))
            return;                
        
        helpdeskMember.getHelpdeskRoles().add(helpdeskRole);
                
        update(helpdeskMember);
        
        log.debug("addHelpdeskRole():: Exit");        
    }

    @Override
    @Transactional(readOnly = false) 
    public void removeHelpdeskRole(Long id, HelpdeskRole helpdeskRole) throws ApiException {
        log.debug("removeHelpdeskRole():: Start with id '{}', helpdeskRole '{}'", id, helpdeskRole);

        HelpdeskMember helpdeskMember = get(id);
        
        if (helpdeskMember == null)
            return;
        
        if (!helpdeskRoleExist(helpdeskRole, helpdeskMember.getHelpdeskRoles()))
            return;                
        
        helpdeskMember.getHelpdeskRoles().remove(helpdeskRole);
                
        update(helpdeskMember);

        log.debug("removeHelpdeskRole():: Exit");
    }        
    
    private boolean helpdeskRoleExist(HelpdeskRole helpdeskRole, Collection<HelpdeskRole> helpdeskRoles) {
        return helpdeskRoles.contains(helpdeskRole);
    }
    
    @Transactional(readOnly = false)
    private void ensureHelpdeskRoles() {
        EntityManager entityManager = getEntityManager();
        
        Query helpdeskRoleExistQuery = entityManager.createNamedQuery("HelpdesMember.helpdeskRoleExist");
        Query insertRoleQuery = entityManager.createNamedQuery("HelpdesMember.insertHelpdeskRole");
        
        for (HelpdeskRole helpdeskRole : HelpdeskRole.values()) {
            helpdeskRoleExistQuery.setParameter(1, helpdeskRole.ordinal());
            int roleCount = Integer.valueOf(helpdeskRoleExistQuery.getSingleResult().toString());
            if (roleCount <= 0) {
                insertRoleQuery.setParameter(1, helpdeskRole.ordinal());
                insertRoleQuery.setParameter(2, helpdeskRole.toString());
                insertRoleQuery.executeUpdate();
            }                
        }
    }
    
    private boolean containsHelpdeskMember(HelpdeskMember helpdeskMember, Collection<Ticket> tickets) {
        if (tickets == null)
            return false;
        
        for (Ticket ticket : tickets) {
            if (ticket.getOwnerId().equals(helpdeskMember.getUserId()))
                return true;            
        }
        
        return false;
    }
    
    private boolean hasRole(HelpdeskRole helpdeskRole, Collection<HelpdeskRole> roles) {
        if (roles == null)
            return false;
        
        for (HelpdeskRole role : roles)
            if (role == helpdeskRole)
                return true;
        
        return false;
    }
}